package med.movyzer.datastructs;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Calendar;

import med.movyzer.util.ExtCalendar;

import org.jfree.data.time.TimeSeries;
import org.jfree.data.xy.XYDataset;

public class MovementSeries {
	public static class Movement {
		private ExtCalendar startTime=new ExtCalendar();
		private ExtCalendar endTime=new ExtCalendar();
		private double intensity;

		public Movement(){

		}
		public Movement(ExtCalendar start,ExtCalendar end){
			this.startTime=(ExtCalendar) start.clone();
			this.endTime=(ExtCalendar) end.clone();
		}

		public Movement(ExtCalendar start,ExtCalendar end, double intensity){
			this.startTime=(ExtCalendar) start.clone();
			this.endTime=(ExtCalendar) end.clone();
			this.intensity=intensity;
		}

		public void setIntensity(double intensity) {
			this.intensity = intensity;
		}
		public double getIntensity() {
			return intensity;
		}
		public void setStartTime(ExtCalendar startTime) {
			this.startTime=(ExtCalendar) startTime.clone();
		}
		public ExtCalendar getStartTime() {
			return startTime;
		}
		public void setEndTime(ExtCalendar endTime) {
			this.endTime = (ExtCalendar) endTime.clone();
		}
		public ExtCalendar getEndTime() {
			return endTime;
		}
		public long getTotalTimeInMillis(){
			return endTime.getTimeInMillis()-startTime.getTimeInMillis();
		}
		public String toString(){
			return "startTime: "+startTime.getTime()+"\nendTime: "+endTime.getTime()+"\n";
		}

	}

	private ArrayList<Movement> list=new ArrayList<Movement>();
	private ExtCalendar startTime=new ExtCalendar();
	private ExtCalendar endTime=new ExtCalendar();
	private int shift=0;
	private double thresholdLM=1.0;


	public MovementSeries() {
	}
	public MovementSeries(ExtCalendar startTime){
		this.startTime=(ExtCalendar) startTime.clone();
	}
	public MovementSeries(ExtCalendar startTime,ExtCalendar endTime){
		this.startTime=(ExtCalendar) startTime.clone();
		this.endTime=(ExtCalendar) endTime.clone();
	}
	public void addMovement(Movement m){
		list.add(m);
		m.getStartTime().add(Calendar.MILLISECOND, shift);
		m.getEndTime().add(Calendar.MILLISECOND, shift);
	}

	public Movement getMovement(int i){
		return list.get(i);
	}

	public int size(){
		return list.size();
	}
	private boolean isPLM(MovementSeries.Movement mov1,MovementSeries.Movement mov2){
		ExtCalendar timeDiff=ExtCalendar.timeDiff(mov1.getStartTime(), mov2.getStartTime());

		if(timeDiff.isBefore(0,1,30,0) && timeDiff.isAfter(0,0,5,0)){
			return true;
		}
		return false;		
	}
	private boolean isLM(MovementSeries.Movement mov){
		ExtCalendar timeDiff=ExtCalendar.timeDiff(mov.getEndTime(), mov.getStartTime());
		if(timeDiff.isBefore(0, 0, 10, 0) && timeDiff.isAfter(0, 0, 0, 500) && mov.getIntensity()>thresholdLM){
			return true;
		}
		return false;
	}

	public MovementSeries getPLMSeries(){
		MovementSeries result=new MovementSeries(startTime,endTime);
		MovementSeries lmMovement=this;

		for(int i=0;i<lmMovement.size();i++){
			MovementSeries.Movement currMovement=lmMovement.getMovement(i);
			MovementSeries.Movement startMovement=currMovement;
			double intensity=currMovement.intensity;
			for(int n=i+1;n<lmMovement.size();n++){
				MovementSeries.Movement nextMovement=lmMovement.getMovement(n);

				if(isPLM(nextMovement,currMovement)==false){
					if(n-i>=4){
						result.addMovement(new MovementSeries.Movement(startMovement.getStartTime(),currMovement.getEndTime(),1));//intensity/(n-i)));
					}
					i=n;
					break;
				}else{
					intensity+=nextMovement.intensity;
				}
				currMovement=nextMovement;
			}
		}

		return result;
	}

	public MovementSeries getLMSeries(double threshold){
		thresholdLM=threshold;
		MovementSeries result=new MovementSeries(startTime, endTime);
		for(int i=0;i<list.size();i++){
			MovementSeries.Movement currMovement=list.get(i);
			if(isLM(currMovement)){
				result.addMovement(currMovement);
			}
		}
		return result;
	}
	
	public void export(String path) throws Exception{
		FileWriter out=new FileWriter(path);
		out.write("Start time;End Time;Intensity\n");
		for(int i=0;i<list.size();i++){
			Movement m=list.get(i);
			out.write(m.getStartTime().getTimeInMillis()+";"+m.getEndTime().getTimeInMillis()+";"+m.getIntensity()+"\n");
		}
		out.close();
	}

	public double getSimilarity(MovementSeries series){
		//calculate the union of the two movement sets
		long totalTime=0;
		//calculate the intersection of the two movement sets
		long overlappTime=0;
		long time=0;

		int j=0;

		for(int i=0;i<this.size();i++){
			while(j<series.size()-1 &&
					series.getMovement(j).getEndTime().before(this.getMovement(i).getStartTime())){

				totalTime+=series.getMovement(j).getTotalTimeInMillis();
				j++;
			}
			time=overlappingTime(this.getMovement(i),series.getMovement(j));
			if(time>0){
				overlappTime+=time;
				totalTime-=time; 	//remove the overlapping time once, because the overlapping time was added twice
			}
			totalTime+=this.getMovement(i).getTotalTimeInMillis();
		}
		totalTime+=series.getMovement(series.size()-1).getTotalTimeInMillis();
		
		if(overlappTime==0 || totalTime==0){
			return 0;
		}
		double perc=totalTime/100;
		double result=overlappTime/perc;
		return result;
	}

	private long overlappingTime(Movement m1,Movement m2){
		if(m1.getStartTime().before(m2.endTime) && m1.getStartTime().after(m2.getStartTime())){
			return m2.getEndTime().getTimeInMillis()-m1.getStartTime().getTimeInMillis();
		}
		if(m2.getStartTime().before(m1.endTime) && m2.getStartTime().after(m1.getStartTime())){
			return m1.getEndTime().getTimeInMillis()-m2.getStartTime().getTimeInMillis();
		}
		return 0;
	}

	public TimeSeries getTimeSeries(double size){
		TimeSeries series = new TimeSeries("Movement");		
		series.addOrUpdate(startTime.getRegularTimePeriod(),0);
		series.addOrUpdate(endTime.getRegularTimePeriod(),0);

		for(int i=0;i<size();i++){			
			series.addOrUpdate(list.get(i).getStartTime().getRegularTimePeriod(), 0);
			series.addOrUpdate(list.get(i).getStartTime().getNextRegularTimePeriod(), list.get(i).getIntensity()*size);
			series.addOrUpdate(list.get(i).getEndTime().getPrevRegularTimePeriod(), list.get(i).getIntensity()*size);
			series.addOrUpdate(list.get(i).getEndTime().getRegularTimePeriod(), 0);
		}

		return series;
	}

	public XYDataset getDataSet(){
		return null;
	}

	public void setList(ArrayList<Movement> list) {
		this.list = list;
		applyShift(this.shift);
	}

	public ArrayList<Movement> getList() {
		return list;
	}

	public void setStartTime(ExtCalendar startTime) {
		this.startTime = (ExtCalendar) startTime.clone();
	}

	public ExtCalendar getStartTime() {
		return startTime;
	}

	public void setEndTime(ExtCalendar endTime) {
		this.endTime = (ExtCalendar) endTime.clone();
	}

	public ExtCalendar getEndTime() {
		return endTime;
	}

	private void applyShift(int milliseconds){
		for(int i=0;i<size();i++){
			Movement m=getMovement(i);
			m.getStartTime().add(Calendar.MILLISECOND, milliseconds);
			m.getEndTime().add(Calendar.MILLISECOND, milliseconds);
		}
	}

	public void setShift(int milliseconds) {
		applyShift(-this.shift);
		this.shift = milliseconds;
		applyShift(this.shift);
	}

	public int getShift() {
		return shift;
	}



}